#include "stdafx.h"
#include <boost/test/auto_unit_test.hpp>
#include <boost/test/execution_monitor.hpp>
#include "bmock.hpp"
#include "bmock_control_tools_mock.hpp"

struct custom_type {
	int i;
};

namespace std {
	istream &operator>>(istream &is,custom_type &ct) {
		is >> ct.i;
		return is;
	}
	ostream &operator<<(ostream &os,const custom_type &ct) {
		os << ct.i;
		return os;
	}
}

static int f() {
	STATIC_MOCK(0,());	
}
static const char* const h() {
	STATIC_MOCK(0,());	
}
static custom_type i1() {
	STATIC_MOCK(0,());	
}
static const custom_type& i2() {
	STATIC_MOCK(0,());	
}
static custom_type& i3() {
	STATIC_MOCK(0,());	
}

struct f_tester {
	static const int expected_ = 10;
	f_tester()
		:result_(0)
	{
		BMOCK_EXPECT_RETURN(expected_,f());
		BMOCK_REPLAY;
	}
	~f_tester() {
		BMOCK_VERIFY;
	}
	int result_;
};

const int f_tester::expected_;


BMOCK_TEST(f_tester,test_ReturnValue) {
	BOOST_CHECK_NO_THROW(result_ = f());
	BOOST_CHECK_EQUAL(expected_,result_);
}

BOOST_AUTO_TEST_CASE(testReturnValueControl) {
	const int expected = 10;
	int actual;
	__BMOCK_CONTROL(c);
	BOOST_CHECK_NO_THROW(__BMOCK_EXPECT_RETURN_C(c,expected,f()));
	__BMOCK_REPLAY_C(c);
	BOOST_CHECK_NO_THROW(actual = f());
	__BMOCK_VERIFY_C(c);
	BOOST_CHECK_EQUAL(expected,actual);
}

BOOST_AUTO_TEST_CASE(testReturnPtrControl) {
	const char *const expected = "abcdefg";
	const char *actual = NULL;;
	__BMOCK_CONTROL(c);
	BOOST_CHECK_NO_THROW(__BMOCK_EXPECT_RETURN_C(c,expected,h()));
	__BMOCK_REPLAY_C(c);
	BOOST_CHECK_NO_THROW(actual = h());
	__BMOCK_VERIFY_C(c);
	BOOST_CHECK_EQUAL(expected,actual);
}

BOOST_AUTO_TEST_CASE(testExpectReturnRequired) {
	__BMOCK_CONTROL(c);
	const bmock::control_tools_ptr t(new bmock::control_tools_mock(c));
	const bmock::location          loc(__FILE__,__LINE__+6);

	__BMOCK_EXPECT_THROW_C(c,new bmock::location(loc),t->error(bmock::message(bmock::CHECK_RETURN_REQUIRED),loc));
	__BMOCK_REPLAY_C(c);
	{
		bmock::test_mode_control c1(bmock::location(__FILE__,__LINE__),t);
		BOOST_CHECK_THROW(__BMOCK_EXPECT_C(c1,f()),bmock::location);
	}
	__BMOCK_VERIFY_C(c);
}

BOOST_AUTO_TEST_CASE(testInvalidReturnValueType) {
	__BMOCK_CONTROL(c);
	const bmock::control_tools_ptr t(new bmock::control_tools_mock(c));
	const bmock::location          loc(__FILE__,__LINE__+7);

	__BMOCK_EXPECT_THROW_C(c,new bmock::location(loc),t->error(bmock::message(bmock::BAD_RETURN_TYPE),loc));
	__BMOCK_REPLAY_C(c);
	{
		const char* c = "wrong";
		bmock::test_mode_control c1(bmock::location(__FILE__,__LINE__),t);
		BOOST_CHECK_THROW(__BMOCK_EXPECT_RETURN_C(c1,c,f()),bmock::location);
	}
	__BMOCK_VERIFY_C(c);
}

struct i_tester {
	static const int exp_i = 129;
	i_tester() {
		expected_.i = exp_i;

	}
	~i_tester() {
		BMOCK_VERIFY;
	}
	custom_type expected_;
};

BMOCK_TEST(i_tester,test_ReturnCustomType) {
	custom_type result = {0};
	BMOCK_EXPECT_RETURN(expected_,i1());
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(result = i1());
	BOOST_CHECK_EQUAL(expected_.i,result.i);
}

BMOCK_TEST(i_tester,test_ReturnCustomTypeConstRef) {
	BMOCK_EXPECT_RETURN(expected_,i2());
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(
		const custom_type &result = i2();
	BOOST_CHECK_EQUAL(expected_.i,result.i);
	BOOST_CHECK_EQUAL(&expected_, &result);
	);
}

BMOCK_TEST(i_tester,test_ReturnCustomTypeRef) {
	BMOCK_EXPECT_RETURN(expected_,i3());
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(
		custom_type &result = i3();
	BOOST_CHECK_EQUAL(expected_.i,result.i);
	BOOST_CHECK_EQUAL(&expected_, &result);
	);
}

static std::string i4() {
	STATIC_MOCK(0,());	
}


BMOCK_TEST(i_tester,test_ReturnString) {
	const std::string expected = "EXPECTED";
	BMOCK_EXPECT_RETURN(expected,i4());
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(
		std::string result = i4();
	BOOST_CHECK_EQUAL(expected,result);
	);
}

struct tester{};

typedef void(*pt2Func)(int);
static void tmpFunc(int) {}

static pt2Func k() {
	STATIC_MOCK(0,());	
}

BMOCK_TEST(tester, test_return_value_function_pointer) {
	pt2Func p;

	BMOCK_EXPECT_RETURN(tmpFunc, k());
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(p = k());
	BOOST_CHECK_EQUAL(p, tmpFunc);
	BMOCK_VERIFY;
}

struct fund_tester {
	fund_tester():ret_val(5){}	
	const int ret_val;
	double casted_ret_val;
private:
	fund_tester& operator=(const fund_tester&) {return *this;} //avoid warning
};

static double g1() {
	STATIC_MOCK(0,());
}

BMOCK_TEST(fund_tester, test_converting_fundamental_type) {
	BMOCK_EXPECT_RETURN(ret_val, g1());
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(casted_ret_val = g1());
	BOOST_CHECK_EQUAL(casted_ret_val, double(ret_val));
	BMOCK_VERIFY;

}

struct casted_from {
	casted_from():x_int(5){}
	int x_int;
};
struct casted_to {
	casted_to(){}
	casted_to(const casted_from& c):x_double(c.x_int) {}
	double x_double;
};

static casted_to g2() {
	STATIC_MOCK(0,());
}


BMOCK_TEST(tester, test_converting_structs) {
	const casted_from c;
	casted_to d;

	BMOCK_EXPECT_RETURN(c,g2());
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(d = g2());
	BOOST_CHECK_EQUAL(d.x_double, double(c.x_int));	
	BMOCK_VERIFY;
}


struct base {
	base(int xx=0):x(xx) {}
	int x;
};

struct derived : public base {
	derived(int xx=0):base(xx),y(0){}
	int y;
};

static base g3() {
	STATIC_MOCK(0,());
}

BMOCK_TEST(tester, test_coverting_base_derived) {
	base b;
	const derived d(4);

	BMOCK_EXPECT_RETURN(d,g3());
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(b = g3());
	BOOST_CHECK_EQUAL(b.x, d.x);
	BMOCK_VERIFY;
}

static base* g4() {
	STATIC_MOCK(0,());
}

BMOCK_TEST(tester, test_converting_base_derived_pointers) {
	base* pb = NULL;
	derived* pd = new derived(4);

	BMOCK_EXPECT_RETURN(pd,g4());
	BMOCK_REPLAY;
	BOOST_CHECK_NO_THROW(pb = g4());
	BOOST_CHECK_EQUAL(pb->x, pd->x);
	BOOST_CHECK_EQUAL(pb,pd);
	delete pd;
	BMOCK_VERIFY;
}

BOOST_AUTO_TEST_CASE(test_invalid_conversion_downcasting) {
	__BMOCK_CONTROL(c);
	const bmock::control_tools_ptr t(new bmock::control_tools_mock(c));
	const bmock::location          loc(__FILE__,__LINE__+6);

	__BMOCK_EXPECT_THROW_C(c,new bmock::location(loc),t->error(bmock::message(bmock::BAD_RETURN_TYPE),loc));
	__BMOCK_REPLAY_C(c);
	{
		bmock::test_mode_control c1(bmock::location(__FILE__,__LINE__),t);
		BOOST_CHECK_THROW(__BMOCK_EXPECT_RETURN_C(c1,base(),f()),bmock::location);
	}
	__BMOCK_VERIFY_C(c);
}

struct NoDefaultConstructor
{
	NoDefaultConstructor(const int x) : x_(x){}
	int x_;
private:	
	NoDefaultConstructor() {}
};

static NoDefaultConstructor g5()
{ 
	STATIC_MOCK(0, ());
}

BMOCK_TEST(tester, test_struct_without_default_constructor)
{
	NoDefaultConstructor ndc_expected(5);
	
	BMOCK_EXPECT_RETURN(ndc_expected, g5());
	BMOCK_REPLAY;
	NoDefaultConstructor ndc_actual = g5();
	BMOCK_VERIFY;
	BOOST_CHECK_EQUAL(ndc_expected.x_, ndc_actual.x_);
}

struct NoCopyConstructor
{
	NoCopyConstructor() : x_(5){}
	int x_;
private:	
	NoCopyConstructor(const NoCopyConstructor&) {}
};

static NoCopyConstructor& g6()
{ 
	STATIC_MOCK(0, ());
}

BMOCK_TEST(tester, test_struct_without_copy_constructor)
{
	NoCopyConstructor ncc_expected;
	
	BMOCK_EXPECT_RETURN(ncc_expected, g6());
	BMOCK_REPLAY;
	NoCopyConstructor& ncc_actual = g6();
	BMOCK_VERIFY;
	BOOST_CHECK_EQUAL(ncc_expected.x_, ncc_actual.x_);
	BOOST_CHECK_EQUAL(&ncc_expected, &ncc_actual);
}
